home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Monster Media 1996 #15
/
Monster Media Number 15 (Monster Media)(July 1996).ISO
/
bbs_util
/
bsrc_260.zip
/
SRC.ZIP
/
JANUS.C
< prev
next >
Wrap
C/C++ Source or Header
|
1996-03-23
|
70KB
|
2,203 lines
/*--------------------------------------------------------------------------*/
/* */
/* */
/* ------------ Bit-Bucket Software, Co. */
/* \ 10001101 / Writers and Distributors of */
/* \ 011110 / Freely Available<tm> Software. */
/* \ 1011 / */
/* ------ */
/* */
/* (C) Copyright 1987-96, Bit Bucket Software Co. */
/* */
/* This module was written by Rick Huebner */
/* BinkleyTerm Janus revision 0.31, 11-2-89 */
/* Full-duplex WaZOO file transfer protocol */
/* */
/* */
/* For complete details of the licensing restrictions, please refer */
/* to the License agreement, which is published in its entirety in */
/* the MAKEFILE and BT.C, and also contained in the file LICENSE.260. */
/* */
/* USE OF THIS FILE IS SUBJECT TO THE RESTRICTIONS CONTAINED IN THE */
/* BINKLEYTERM LICENSING AGREEMENT. IF YOU DO NOT FIND THE TEXT OF */
/* THIS AGREEMENT IN ANY OF THE AFOREMENTIONED FILES, OR IF YOU DO */
/* NOT HAVE THESE FILES, YOU SHOULD IMMEDIATELY CONTACT BIT BUCKET */
/* SOFTWARE CO. AT ONE OF THE ADDRESSES LISTED BELOW. IN NO EVENT */
/* SHOULD YOU PROCEED TO USE THIS FILE WITHOUT HAVING ACCEPTED THE */
/* TERMS OF THE BINKLEYTERM LICENSING AGREEMENT, OR SUCH OTHER */
/* AGREEMENT AS YOU ARE ABLE TO REACH WITH BIT BUCKET SOFTWARE, CO. */
/* */
/* */
/* You can contact Bit Bucket Software Co. at any one of the following */
/* addresses: */
/* */
/* Bit Bucket Software Co. FidoNet 1:104/501, 1:343/491 */
/* P.O. Box 460398 AlterNet 7:42/1491 */
/* Aurora, CO 80046 BBS-Net 86:2030/1 */
/* Internet f491.n343.z1.fidonet.org */
/* */
/* Please feel free to contact us at any time to share your comments about */
/* our software and/or licensing policies. */
/* */
/*--------------------------------------------------------------------------*/
/* Include this file before any other includes or defines! */
#include "includes.h"
#include "janus.h"
/* Private routines */
static void LOCALFUNC getfname (word);
static void LOCALFUNC sendpkt (byte *, int, int);
static void LOCALFUNC sendpkt32 (byte *, int, int);
static void LOCALFUNC txbyte (byte);
static long LOCALFUNC procfname (void);
static byte LOCALFUNC rcvpkt (void);
static void LOCALFUNC rxclose (word);
static void LOCALFUNC endbatch (void);
static void LOCALFUNC update_y (void);
static void LOCALFUNC long_set_timer (long *, unsigned int);
static int LOCALFUNC long_time_gone (long *);
static int LOCALFUNC rcvrawbyte (void);
static int LOCALFUNC rxbyte (void);
static int LOCALFUNC get_filereq (byte);
static int record_reqfile (char *); /* ** NOT ** LOCALFUNC!!! */
static int timeof_reqfile (long); /* ** NOT ** LOCALFUNC!!! */
static byte LOCALFUNC get_reqname (byte);
static void LOCALFUNC mark_done (char *);
/* Common with HYDRA.C */
void j_message (unsigned int, char *,...);
void j_status (char *,...);
void j_msgend (short);
int j_error (char *, char *);
void xfer_summary (char *, char *, long *, short);
void update_status (long *, long *, long, int *, short);
long through (long *, long *);
/* Private data. I know, proper software design says you shouldn't make data */
/* global unless you really need to. In this case speed and code size make */
/* it more important to avoid constantly pushing & popping arguments. */
static char *GenericError = "!%s";
static char *ReqTmpTempl = "JANUSREQ.%02x";
static char ReqTmp[16];
static char *Rxbuf; /* Address of packet reception buffer */
static char *Txfname; /* Full path of file we're sending */
static char *Rxfname; /* Full path of file we're receiving */
static byte *Rxbufptr; /* Current position within packet reception buffer */
static byte *Rxbufmax; /* Upper bound of packet reception buffer */
static byte Do_after; /* What to do with file being sent when we're done */
static byte WaitFlag; /* Tells rcvrawbyte() whether or not to wait */
static byte SharedCap; /* Capability bits both sides have in common */
static int Txfile; /* File handle of file we're sending */
static int Rxfile; /* File handle of file we're receiving */
static int ReqRecorded; /* Number of files obtained by this request */
static word TimeoutSecs; /* How long to wait for various things */
static int Rxblklen; /* Length of data in last data block packet recvd */
static int Next_y; /* Number of next available line on screen */
static short Tx_y; /* Line number of file transmission status display */
static short Rx_y; /* Line number of file reception status display */
static long Txlen; /* Total length of file we're sending */
static long Rxlen; /* Total length of file we're receiving */
static long Rxfiletime; /* Timestamp of file we're receiving */
static long Diskavail; /* Bytes available in upload directory */
static long Txsttime; /* Time at which we started sending current file */
static long Rxsttime; /* Time at which we started receiving current file */
static int EMSI_flag; /* Currently in EMSI-Session ? */
static int EMSI_aka; /* Current aka to send mail to */
static int EMSI_raka; /* Current aka to request files from */
/*****************************************************************************/
/* Super-duper neato-whizbang full-duplex streaming ACKless batch file */
/* transfer protocol for use in WaZOO mail sessions */
/*****************************************************************************/
void
Janus (void)
{
byte xstate; /* Current file transmission state */
byte rstate; /* Current file reception state */
byte pkttype; /* Type of packet last received */
byte tx_inhibit; /* Flag to wait and send after done receiving */
char *holdname; /* Name of hold area */
byte fsent; /* Did we manage to send anything this session? */
byte sending_req; /* Are we currently sending requested files? */
byte attempting_req; /* Are we waiting for the sender to start our req? */
byte req_started; /* Has the sender started servicing our request? */
int txoldeta; /* Last transmission ETA displayed */
int rxoldeta; /* Last reception ETA displayed */
word blklen; /* Length of last data block sent */
word txblklen; /* Size of data block to try to send this time */
word txblkmax; /* Max size of data block to send at this speed */
word goodneeded; /* # good bytes to send before upping txblklen */
word goodbytes; /* Number of good bytes sent at this block size */
word rpos_count; /* Number of RPOS packets sent at this position */
long xmit_retry; /* Time to retransmit lost FNAMEPKT or EOF packet */
long txpos; /* Current position within file we're sending */
long lasttx; /* Position within file of last data block we sent */
long txstpos; /* Initial data position of file we're sending */
long rxstpos; /* Initial data position of file we're receiving */
long txoldpos; /* Last transmission file position displayed */
long rxoldpos; /* Last reception file position displayed */
long rpos_retry; /* Time at which to retry RPOS packet */
long brain_dead; /* Time at which to give up on other computer */
long rpos_sttime = 0; /* Time at which we started current RPOS sequence */
long last_rpostime; /* Timetag of last RPOS which we performed */
long last_blkpos; /* File position of last out-of-sequence BLKPKT */
FILE *reqfile; /* File handle for .REQ file */
sprintf (ReqTmp, ReqTmpTempl, TaskNumber);
set_prior (3); /* Time Critical */
XON_DISABLE ();
if (un_attended && fullscreen)
{
clear_filetransfer ();
sb_show ();
}
else
{
set_xy (NULL);
Next_y = locate_y;
}
Tx_y = Rx_y = 0;
SharedCap = 0;
/*------------------------------------------------------------------------*/
/* Allocate memory */
/*------------------------------------------------------------------------*/
Rxbuf = (char *) Txbuf + 4096 + 8;
Txfname = Rxfname = NULL;
if (((Txfname = malloc (PATHLEN)) == NULL)
|| ((Rxfname = malloc (PATHLEN)) == NULL))
{
status_line (MSG_TXT (M_MEM_ERROR));
mdm_hangup ();
goto freemem;
}
Rxbufmax = (byte *) (Rxbuf + BUFMAX + 8);
/*------------------------------------------------------------------------*/
/* Initialize file transmission variables */
/*------------------------------------------------------------------------*/
if (!no_EMSI_Session)
{
EMSI_flag = TRUE;
EMSI_aka = EMSI_raka = 0;
called_addr = remote_akas[EMSI_aka];
}
else
EMSI_flag = FALSE;
tx_inhibit = FALSE;
last_rpostime = last_blkpos = lasttx = txstpos = rxstpos = xmit_retry = 0L;
long_set_timer (&brain_dead, 120);
if (cur_baud.rate_value > 9600L)
{
TimeoutSecs = 30;
txblkmax = BUFMAX;
}
else
{
TimeoutSecs = (unsigned int) (40960L / cur_baud.rate_value);
if (TimeoutSecs < 30)
TimeoutSecs = 30;
txblkmax = (int)cur_baud.rate_value / 300 * 128;
if (txblkmax > BUFMAX)
txblkmax = BUFMAX;
}
txblklen = txblkmax;
goodbytes = goodneeded = 0;
Txfile = -1;
sending_req = fsent = FALSE;
xstate = XSENDFNAME;
getfname (INITIAL_XFER);
/*------------------------------------------------------------------------*/
/* Initialize file reception variables */
/*------------------------------------------------------------------------*/
holdname = HoldAreaNameMunge (&called_addr);
(void) sprintf (Abortlog_name, "%s%s.Z\0", holdname, Hex_Addr_Str (&called_addr));
if ((Diskavail = zfree (CURRENT.sc_Inbound)) <= 0L)
Diskavail = 0x7FFFFFF;
Rxbufptr = NULL;
rpos_retry = rpos_count = 0;
attempting_req = req_started = FALSE;
rstate = RRCVFNAME;
/*------------------------------------------------------------------------*/
/* Send and/or receive stuff until we're done with both */
/*------------------------------------------------------------------------*/
do
{ /* while (xstate || rstate) */
/*---------------------------------------------------------------------*/
/* If nothing useful (i.e. sending or receiving good data block) has */
/* happened within the last 2 minutes, give up in disgust */
/*---------------------------------------------------------------------*/
if (long_time_gone (&brain_dead))
{
j_status (MSG_TXT (M_OTHER_DIED)); /* "He's dead, Jim." */
goto giveup;
}
/*---------------------------------------------------------------------*/
/* If we're tired of waiting for an ACK, try again */
/*---------------------------------------------------------------------*/
if (xmit_retry)
{
if (long_time_gone (&xmit_retry))
{
j_message (Tx_y, MSG_TXT (M_TIMEOUT));
xmit_retry = 0L;
switch (xstate)
{
case XRCVFNACK:
xstate = XSENDFNAME;
break;
case XRCVFRNAKACK:
xstate = XSENDFREQNAK;
break;
case XRCVEOFACK:
errno = 0;
if (lseek (Txfile, txpos = lasttx, SEEK_SET) == -1L)
{
(void) j_error (MSG_TXT (M_SEEK_MSG), Txfname);
goto giveup;
}
xstate = XSENDBLK;
break;
}
}
}
/*---------------------------------------------------------------------*/
/* Transmit next part of file, if any */
/*---------------------------------------------------------------------*/
switch (xstate)
{
case XSENDBLK:
if (tx_inhibit)
break;
#ifdef GENERIC
lasttx = txpos;
Txbuf[0] = txpos & 0xff;
Txbuf[1] = (txpos >> 8) & 0xff;
Txbuf[2] = (txpos >> 16) & 0xff;
Txbuf[3] = (txpos >> 24) & 0xff;
#else
*((long *) Txbuf) = lasttx = txpos;
#endif
errno = 0;
blklen = read (Txfile, Txbuf + sizeof (txpos), txblklen);
if (j_error (MSG_TXT (M_READ_MSG), Txfname))
goto giveup;
txpos += blklen;
sendpkt (Txbuf, sizeof (txpos) + blklen, BLKPKT);
update_status (&txpos, &txoldpos, Txlen - txpos, &txoldeta, Tx_y);
fsent = TRUE;
if (txpos >= Txlen || blklen < txblklen)
{
long_set_timer (&xmit_retry, TimeoutSecs);
xstate = XRCVEOFACK;
}
else
long_set_timer (&brain_dead, 120);
if (txblklen < txblkmax && (goodbytes += txblklen) >= goodneeded)
{
txblklen <<= 1;
goodbytes = 0;
}
break;
case XSENDFNAME:
blklen = (int) (strchr (strchr ((char *) Txbuf, '\0') + 1, '\0') - (char *) Txbuf) + 1;
Txbuf[blklen++] = OURCAP;
sendpkt (Txbuf, blklen, FNAMEPKT);
txoldpos = txoldeta = -1;
long_set_timer (&xmit_retry, TimeoutSecs);
xstate = XRCVFNACK;
break;
case XSENDFREQNAK:
sendpkt (NULL, 0, FREQNAKPKT);
long_set_timer (&xmit_retry, TimeoutSecs);
xstate = XRCVFRNAKACK;
break;
}
/*---------------------------------------------------------------------*/
/* Catch up on our reading; receive and handle all outstanding packets */
/*---------------------------------------------------------------------*/
while ((pkttype = rcvpkt ()) != 0)
{
if (pkttype != BADPKT)
long_set_timer (&brain_dead, 120);
switch (pkttype)
{
/*---------------------------------------------------------------*/
/* File data block or munged block */
/*---------------------------------------------------------------*/
case BADPKT:
case BLKPKT:
if (rstate == RRCVBLK)
{
long t;
#ifdef GENERIC
t = (long) Rxbuf[0] +
((long) Rxbuf[1] << 8) +
((long) Rxbuf[2] << 16) +
((long) Rxbuf[3] << 24);
#else
t = *(long *) Rxbuf;
#endif
if (pkttype == BADPKT || (t != Rxpos))
{
if (pkttype == BLKPKT)
{
if (t < last_blkpos)
rpos_retry = rpos_count = 0;
last_blkpos = t;
}
if (long_time_gone (&rpos_retry))
{
/*---------------------------------------------------*/
/* If we're the called machine, and we're trying to */
/* send stuff, and it seems to be screwing up our */
/* ability to receive stuff, maybe this connection */
/* just can't hack full-duplex. Try waiting till */
/* the sending system finishes before sending our */
/* stuff to it */
/*---------------------------------------------------*/
if (rpos_count > 4)
{
if (xstate && !isOriginator && !tx_inhibit)
{
tx_inhibit = TRUE;
j_status (MSG_TXT (M_GOING_ONE_WAY));
}
rpos_count = 0;
}
if (++rpos_count == 1)
(void) time ((time_t *) & rpos_sttime);
j_message (Rx_y, MSG_TXT (M_J_BAD_PACKET), Rxpos);
#ifdef GENERIC
Rxbuf[0] = Rxpos & 0xff;
Rxbuf[1] = (Rxpos >> 8) & 0xff;
Rxbuf[2] = (Rxpos >> 16) & 0xff;
Rxbuf[3] = (Rxpos >> 24) & 0xff;
Rxbuf[4] = rpos_sttime & 0xff;
Rxbuf[5] = (rpos_sttime >> 8) & 0xff;
Rxbuf[6] = (rpos_sttime >> 16) & 0xff;
Rxbuf[7] = (rpos_sttime >> 24) & 0xff;
#else
*((long *) Rxbuf) = Rxpos;
*((long *) (Rxbuf + sizeof (Rxpos))) = rpos_sttime;
#endif
sendpkt ((byte *) Rxbuf, sizeof (Rxpos) + sizeof (rpos_sttime), RPOSPKT);
long_set_timer (&rpos_retry, TimeoutSecs / 2);
}
}
else
{
last_blkpos = Rxpos;
rpos_retry = rpos_count = 0;
errno = 0;
(void) write (Rxfile, Rxbuf + sizeof (Rxpos), Rxblklen -= sizeof (Rxpos));
if (j_error (MSG_TXT (M_WRITE_MSG), Rxfname))
goto giveup;
Diskavail -= Rxblklen;
Rxpos += Rxblklen;
update_status (&Rxpos, &rxoldpos, Rxlen - Rxpos, &rxoldeta, Rx_y);
if (Rxpos >= Rxlen)
{
long Rxtime;
rxclose (GOOD_XFER);
Rxlen -= rxstpos;
Rxtime = through (&Rxlen, &Rxsttime);
j_status ("%s-J%s %s", MSG_TXT (M_FILE_RECEIVED), (SharedCap & CANCRC32) ? "/32" : " ", Rxfname);
j_msgend (Rx_y);
update_files (0, Rxfname, Rxlen, Rxtime, 0);
rstate = RRCVFNAME;
}
}
}
if (rstate == RRCVFNAME)
sendpkt (NULL, 0, EOFACKPKT);
break;
/*---------------------------------------------------------------*/
/* Name and other data for next file to receive */
/*---------------------------------------------------------------*/
case FNAMEPKT:
if (rstate == RRCVFNAME)
Rxpos = rxstpos = procfname ();
if (!Rxfname[0] && get_filereq (req_started))
{
sendpkt ((byte *) Rxbuf, strlen (Rxbuf) + 2, FREQPKT);
attempting_req = TRUE;
req_started = FALSE;
}
else
{
if (attempting_req)
{
attempting_req = FALSE;
req_started = TRUE;
}
#ifdef GENERIC
Rxbuf[0] = Rxpos & 0xff;
Rxbuf[1] = (Rxpos >> 8) & 0xff;
Rxbuf[2] = (Rxpos >> 16) & 0xff;
Rxbuf[3] = (Rxpos >> 24) & 0xff;
Rxbuf[4] = SharedCap;
#else
*((long *) Rxbuf) = Rxpos;
Rxbuf[sizeof (Rxpos)] = (char) SharedCap;
#endif
sendpkt ((byte *) Rxbuf, sizeof (Rxpos) + 1, FNACKPKT);
rxoldpos = rxoldeta = -1;
if (Rxpos > -1)
rstate = (byte) ((Rxfname[0]) ? RRCVBLK : RDONE);
else
j_status (MSG_TXT (M_REFUSING), Rxfname);
if (!rstate)
tx_inhibit = FALSE;
if (!(xstate || rstate))
goto breakout;
}
break;
/*---------------------------------------------------------------*/
/* ACK to filename packet we just sent */
/*---------------------------------------------------------------*/
case FNACKPKT:
if (xstate == XRCVFNACK)
{
xmit_retry = 0L;
if (Txfname[0])
{
#ifdef GENERIC
SharedCap = (Rxblklen > sizeof (long)) ? Rxbuf[4] : 0;
txpos = (long) Rxbuf[0] +
((long) Rxbuf[1] << 8) +
((long) Rxbuf[2] << 16) +
((long) Rxbuf[3] << 24);
#else
SharedCap = (byte) ((Rxblklen > sizeof (long)) ? Rxbuf[sizeof (long)] : 0);
txpos = *((long *) Rxbuf);
#endif
if (txpos > -1L)
{
if (txpos)
status_line (MSG_TXT (M_SYNCHRONIZING), txpos);
errno = 0;
if (lseek (Txfile, txstpos = txpos, SEEK_SET) == -1L)
{
(void) j_error (MSG_TXT (M_SEEK_MSG), Txfname);
goto giveup;
}
xstate = XSENDBLK;
}
else
{
j_status (MSG_TXT (M_REMOTE_REFUSED), Txfname);
if (sending_req)
{
if (!(sending_req = get_reqname (FALSE)))
getfname (GOOD_XFER);
}
else
{
Do_after = NOTHING_AFTER;
getfname (GOOD_XFER);
}
xstate = XSENDFNAME;
}
}
else
{
sent_mail = 1;
xstate = XDONE;
}
}
if (!(xstate || rstate))
goto breakout;
break;
/*---------------------------------------------------------------*/
/* Request to send more stuff rather than end batch just yet */
/*---------------------------------------------------------------*/
case FREQPKT:
if (xstate == XRCVFNACK)
{
xmit_retry = 0L;
SharedCap = *(strchr (Rxbuf, '\0') + 1);
(void) sprintf ((char *) Txbuf, request_template, CURRENT.sc_Inbound,
Hex_Addr_Str (&(alias[0])), TaskNumber);
errno = 0;
reqfile = fopen ((char *) Txbuf, write_ascii);
if (reqfile != (FILE *) NULL)
errno = 0;
(void) j_error (MSG_TXT (M_OPEN_MSG), (char *) Txbuf);
(void) fputs (Rxbuf, reqfile);
(void) fputs ("\n", reqfile);
(void) fclose (reqfile);
(void) unlink (ReqTmp);
ReqRecorded = 0; /* counted by record_reqfile */
(void) respond_to_file_requests (0, record_reqfile, timeof_reqfile);
CURRENT.rq_Limit -= ReqRecorded;
if ((sending_req = get_reqname (TRUE)) != 0)
xstate = XSENDFNAME;
else
xstate = XSENDFREQNAK;
}
break;
/*---------------------------------------------------------------*/
/* Our last file request didn't match anything; move on to next */
/*---------------------------------------------------------------*/
case FREQNAKPKT:
attempting_req = FALSE;
req_started = TRUE;
sendpkt (NULL, 0, FRNAKACKPKT);
break;
/*---------------------------------------------------------------*/
/* ACK to no matching files for request error; try to end again */
/*---------------------------------------------------------------*/
case FRNAKACKPKT:
if (xstate == XRCVFRNAKACK)
{
xmit_retry = 0L;
getfname (GOOD_XFER);
xstate = XSENDFNAME;
}
break;
/*---------------------------------------------------------------*/
/* ACK to last data block in file */
/*---------------------------------------------------------------*/
case EOFACKPKT:
if (xstate == XRCVEOFACK || xstate == XRCVFNACK)
{
xmit_retry = 0L;
if (xstate == XRCVEOFACK)
{
long Txtime;
Txlen -= txstpos;
Txtime = through (&Txlen, &Txsttime);
j_status ("%s-J%s %s", MSG_TXT (M_FILE_SENT), (SharedCap & CANCRC32) ? "/32" : " ", Txfname);
j_msgend (Tx_y);
update_files (1, Txfname, Txlen, Txtime, 0);
if (sending_req)
{
if (!(sending_req = get_reqname (FALSE)))
getfname (GOOD_XFER);
}
else
getfname (GOOD_XFER);
}
xstate = XSENDFNAME;
}
break;
/*---------------------------------------------------------------*/
/* Receiver says "let's try that again." */
/*---------------------------------------------------------------*/
case RPOSPKT:
if (xstate == XSENDBLK || xstate == XRCVEOFACK)
{
long t;
#ifdef GENERIC
t = (long) Rxbuf[4] +
((long) Rxbuf[5] << 8) +
((long) Rxbuf[6] << 16) +
((long) Rxbuf[7] << 24);
#else
t = *((long *) (Rxbuf + sizeof (txpos)));
#endif
if (t != last_rpostime)
{
last_rpostime = t;
xmit_retry = 0L;
CLEAR_OUTBOUND ();
errno = 0;
#ifdef GENERIC
lasttx = (long) Rxbuf[0] +
((long) Rxbuf[1] << 8) +
((long) Rxbuf[2] << 16) +
((long) Rxbuf[3] << 24);
#else
lasttx = *((long *) Rxbuf);
#endif
if (lseek (Txfile, txpos = lasttx, SEEK_SET) == -1L)
{
(void) j_error (MSG_TXT (M_SEEK_MSG), Txfname);
goto giveup;
}
j_status (MSG_TXT (M_SYNCHRONIZING), txpos);
txblklen >>= 2;
if (txblklen < 64)
txblklen = 64;
goodbytes = 0;
goodneeded += 1024;
if (goodneeded > 8192)
goodneeded = 8192;
xstate = XSENDBLK;
}
}
break;
/*---------------------------------------------------------------*/
/* Debris from end of previous Janus session; ignore it */
/*---------------------------------------------------------------*/
case HALTACKPKT:
break;
/*---------------------------------------------------------------*/
/* Abort the transfer and quit */
/*---------------------------------------------------------------*/
default:
j_status (MSG_TXT (M_UNKNOWN_PACKET), pkttype);
/* fallthrough */
case HALTPKT:
giveup:
j_status (MSG_TXT (M_SESSION_ABORT));
if (Txfname[0])
getfname (ABORT_XFER);
if (rstate == RRCVBLK)
{
rxclose (FAILED_XFER);
}
goto abortxfer;
} /* switch (pkttype) */
} /* while (pkttype) */
}
while (xstate || rstate);
/*------------------------------------------------------------------------*/
/* All done; make sure other end is also finished (one way or another) */
/*------------------------------------------------------------------------*/
breakout:
if (!fsent)
j_status (MSG_TXT (M_NOTHING_TO_SEND), Full_Addr_Str (&called_addr));
abortxfer:
endbatch ();
/*------------------------------------------------------------------------*/
/* Release allocated memory */
/*------------------------------------------------------------------------*/
freemem:
if (Txfname)
free (Txfname);
if (Rxfname)
free (Rxfname);
set_prior (4); /* Always High */
}
/*****************************************************************************/
/* Get name and info for next file to be transmitted, if any, and build */
/* FNAMEPKT. Packet contents as per ZModem filename info packet, to allow */
/* use of same method of aborted-transfer recovery. If there are no more */
/* files to be sent, build FNAMEPKT with null filename. Also open file and */
/* set up for transmission. Set Txfname, Txfile, Txlen. Txbuf must not be */
/* modified until FNACKPKT is received. */
/*****************************************************************************/
static void LOCALFUNC
getfname (word xfer_flag)
{
static byte floflag, bad_xfers;
static char outboundname[PATHLEN];
static long floname_pos;
static FILE *flofile;
char *holdname;
register char *p;
int i;
long curr_pos;
struct stat f;
/*------------------------------------------------------------------------*/
/* Initialize static variables on first call of the batch */
/*------------------------------------------------------------------------*/
if (xfer_flag == INITIAL_XFER)
{
if (EMSI_flag)
status_line (MSG_TXT (M_EMSI_PROC_NODE),
Full_Addr_Str (&remote_akas[EMSI_aka]),
HoldAreaNameMunge (&remote_akas[EMSI_aka]));
floflag = outboundname[0] = '\0';
flofile = NULL;
}
else
/*------------------------------------------------------------------------*/
/* If we were already sending a file, close it and clean up */
/*------------------------------------------------------------------------*/
if (Txfile != -1)
{
errno = 0;
(void) close (Txfile);
Txfile = -1;
/*---------------------------------------------------------------------*/
/* If xfer completed, do post-xfer cleanup */
/*---------------------------------------------------------------------*/
if (xfer_flag == GOOD_XFER)
{
/*------------------------------------------------------------------*/
/* Perform post-xfer file massaging if neccessary */
/*------------------------------------------------------------------*/
switch (Do_after)
{
case DELETE_AFTER:
case SHOW_DELETE_AFTER:
j_status (MSG_TXT (M_UNLINKING_MSG), Txfname);
(void) unlink (Txfname);
break;
case TRUNC_AFTER:
j_status (MSG_TXT (M_TRUNC_MSG), Txfname);
Txfile = open (Txfname, O_TRUNC | O_RDWR, S_IREAD | S_IWRITE);
if (Txfile != -1)
errno = 0;
(void) j_error (MSG_TXT (M_TRUNC_MSG), Txfname);
(void) close (Txfile);
Txfile = -1;
}
/*------------------------------------------------------------------*/
/* If processing .?LO file, flag filename as sent (name[0] = '~') */
/*------------------------------------------------------------------*/
skipname:
if (floflag)
{
curr_pos = ftell (flofile);
if (curr_pos == -1L)
(void) j_error (MSG_TXT (M_SEEK_MSG), outboundname);
if (fseek (flofile, floname_pos, SEEK_SET) == -1L)
(void) j_error (MSG_TXT (M_SEEK_MSG), outboundname);
if (fputc (Txfname[0] = '~', flofile) != EOF)
errno = 0;
(void) j_error (MSG_TXT (M_WRITE_MSG), outboundname);
if (fseek (flofile, curr_pos, SEEK_SET) == -1L)
(void) j_error (MSG_TXT (M_SEEK_MSG), outboundname);
}
}
else
{
abort:
++bad_xfers;
}
}
/*------------------------------------------------------------------------*/
/* Find next file to be sent and build FNAMEPKT. If reading .FLO-type */
/* file get next entry from it; otherwise check for next .OUT/.FLO file */
/*------------------------------------------------------------------------*/
if (EMSI_flag)
holdname = HoldAreaNameMunge (&remote_akas[EMSI_aka]);
else
holdname = HoldAreaNameMunge (&called_addr);
next_aka:
if (!floflag)
{
/*---------------------------------------------------------------------*/
/* If first getfname() for this batch, init filename to .OUT */
/*---------------------------------------------------------------------*/
if (!outboundname[0])
{
if (!EMSI_flag)
(void) sprintf (outboundname,
"%s%s.OUT",
holdname,
Hex_Addr_Str (&called_addr));
else
(void) sprintf (outboundname,
"%s%s.OUT",
holdname,
Hex_Addr_Str (&remote_akas[EMSI_aka]));
*ext_flags = 'O';
}
/*---------------------------------------------------------------------*/
/* Increment outbound filename until match found or all checked */
/* .OUT->.DUT->.CUT->.HUT->.FLO->.DLO->.CLO->.HLO->null name */
/*---------------------------------------------------------------------*/
else
{
nxtout:
p = strchr (outboundname, '\0') - 3;
for (i = 0; i < NUM_FLAGS; ++i)
if (ext_flags[i] == *p)
break;
if (i < NUM_FLAGS - 1)
{
*p = ext_flags[i + 1];
#ifndef JACK_DECKER
if (isOriginator && *p == 'H')
goto nxtout;
#endif
}
else
{
/*---------------------------------------------------------------*/
/* Finished ?,D,C,H sequence; wrap .OUT->.FLO, or .FLO->done */
/*---------------------------------------------------------------*/
if (!floflag)
{
*p++ = *ext_flags = 'F';
*p++ = 'L';
*p = 'O';
++floflag;
}
else
{
outboundname[0] = Txfname[0] = Txbuf[0] = Txbuf[1] = floflag = '\0';
if (EMSI_flag && (++EMSI_aka < num_rakas))
{
holdname = HoldAreaNameMunge (&remote_akas[EMSI_aka]);
status_line (MSG_TXT (M_EMSI_PROC_NODE),
Full_Addr_Str (&remote_akas[EMSI_aka]),
holdname);
goto next_aka;
}
}
}
}
/*---------------------------------------------------------------------*/
/* Check potential outbound name; if file doesn't exist keep looking */
/*---------------------------------------------------------------------*/
if (outboundname[0])
{
if (!dexists (outboundname))
goto nxtout;
if (floflag)
goto rdflo;
(void) strcpy (Txfname, outboundname);
/*------------------------------------------------------------------*/
/* Start FNAMEPKT using .PKT alias */
/*------------------------------------------------------------------*/
invent_pkt_name ((char *) Txbuf);
Do_after = DELETE_AFTER;
}
/*---------------------------------------------------------------------*/
/* Read and process next entry from .?LO-type file */
/*---------------------------------------------------------------------*/
}
else
{
rdflo:
/*---------------------------------------------------------------------*/
/* Open .?LO file for processing if neccessary */
/*---------------------------------------------------------------------*/
if (!flofile)
{
bad_xfers = 0;
errno = 0;
flofile = share_fopen (outboundname, "rb+", DENY_WRITE);
if (flofile == (FILE *) NULL)
{
j_error (MSG_TXT (M_OPEN_MSG), outboundname);
goto nxtout;
}
}
errno = 0;
floname_pos = ftell (flofile);
if (floname_pos == -1L)
(void) j_error (MSG_TXT (M_SEEK_MSG), outboundname);
if (fgets (p = Txfname, PATHLEN, flofile))
{
/*------------------------------------------------------------------*/
/* Got an attached file name; check for handling flags, fix up name */
/*------------------------------------------------------------------*/
while (*p > ' ')
++p;
*p = '\0';
switch (Txfname[0])
{
case '\0':
case '~':
case ';':
goto rdflo;
case TRUNC_AFTER:
case DELETE_AFTER:
case SHOW_DELETE_AFTER:
Do_after = Txfname[0];
(void) strcpy (Txfname, Txfname + 1);
break;
default:
Do_after = NOTHING_AFTER;
break;
}
/*------------------------------------------------------------------*/
/* Start FNAMEPKT with simple filename */
/*------------------------------------------------------------------*/
while (p >= Txfname && *p != '\\' && *p != ':')
--p;
(void) strcpy ((char *) Txbuf, ++p);
}
else
{
/*------------------------------------------------------------------*/
/* Finished reading this .?LO file; clean up and look for another */
/*------------------------------------------------------------------*/
errno = 0;
(void) fclose (flofile);
flofile = NULL;
if (!bad_xfers)
{
(void) unlink (outboundname);
}
goto nxtout;
}
}
/*------------------------------------------------------------------------*/
/* If we managed to find a valid file to transmit, open it, finish */
/* FNAMEPKT, and print nice message for the sysop. */
/*------------------------------------------------------------------------*/
if (Txfname[0])
{
if (xfer_flag == ABORT_XFER)
goto abort;
j_status (MSG_TXT (M_SENDING), Txfname);
errno = 0;
Txfile = share_open (Txfname, O_RDONLY | O_BINARY, DENY_WRITE);
if (Txfile != -1)
errno = 0;
if (j_error (MSG_TXT (M_OPEN_MSG), Txfname))
goto skipname;
if (isatty (Txfile)) /* Check for character devices */
{
(void) close (Txfile); /* return errors if it is the case */
errno = 1;
(void) j_error (MSG_TXT (M_DEVICE_MSG), Txfname);
goto skipname;
}
(void) stat (Txfname, &f);
#ifdef ANSI_TIME_T
f.st_mtime -= ANSI_TIME_T_DELTA;
#endif
(void) sprintf (strchr ((char *) Txbuf, '\0') + 1, "%lu %lo %o", Txlen = f.st_size, f.st_mtime, f.st_mode);
p = strchr (Txfname, '\0');
while (p >= Txfname && *p != ':' && *p != '\\')
--p;
if (!un_attended || !fullscreen)
Tx_y = Next_y;
else
Tx_y = 1;
xfer_summary (MSG_TXT (M_SEND), ++p, &Txlen, Tx_y);
(void) time ((time_t *) & Txsttime);
}
}
/*****************************************************************************/
/* Build and send a packet of any type. */
/* Packet structure is: PKTSTRT,contents,packet_type,PKTEND,crc */
/* CRC is computed from contents and packet_type only; if PKTSTRT or PKTEND */
/* get munged we'll never even find the CRC. */
/*****************************************************************************/
static void LOCALFUNC
sendpkt (register byte * buf, int len, int type)
{
register word crc;
if ((SharedCap & CANCRC32) && type != FNAMEPKT)
sendpkt32 (buf, len, type);
else
{
BUFFER_BYTE (DLE);
BUFFER_BYTE (PKTSTRTCHR ^ 0x40);
crc = 0;
while (--len >= 0)
{
txbyte (*buf);
crc = xcrc (crc, ((word) (*buf++)));
}
BUFFER_BYTE ((byte) type);
crc = xcrc (crc, type);
BUFFER_BYTE (DLE);
BUFFER_BYTE (PKTENDCHR ^ 0x40);
txbyte ((byte) (crc >> 8));
txbyte ((byte) (crc & 0xFF));
UNBUFFER_BYTES ();
}
}
/*****************************************************************************/
/* Build and send a packet using 32-bit CRC; same as sendpkt in other ways */
/*****************************************************************************/
static void LOCALFUNC
sendpkt32 (register byte * buf, register int len, int type)
{
unsigned long crc32;
BUFFER_BYTE (DLE);
BUFFER_BYTE (PKTSTRTCHR32 ^ 0x40);
crc32 = 0xFFFFFFFF;
while (--len >= 0)
{
txbyte (*buf);
crc32 = Z_32UpdateCRC (((word) * buf), crc32);
++buf;
}
BUFFER_BYTE ((byte) type);
crc32 = Z_32UpdateCRC (type, crc32);
BUFFER_BYTE (DLE);
BUFFER_BYTE (PKTENDCHR ^ 0x40);
txbyte ((byte) (crc32 >> 24));
txbyte ((byte) ((crc32 >> 16) & 0xFF));
txbyte ((byte) ((crc32 >> 8) & 0xFF));
txbyte ((byte) (crc32 & 0xFF));
UNBUFFER_BYTES ();
}
/*****************************************************************************/
/* Transmit cooked escaped byte(s) corresponding to raw input byte. Escape */
/* DLE, XON, and XOFF using DLE prefix byte and ^ 0x40. Also escape */
/* CR-after-'@' to avoid Telenet/PC-Pursuit problems. */
/*****************************************************************************/
static void LOCALFUNC
txbyte (register byte c)
{
static byte lastsent;
switch (c)
{
case CR:
if (lastsent != '@')
goto sendit;
/* fallthrough */
case DLE:
case XON:
case XOFF:
BUFFER_BYTE (DLE);
c ^= 0x40;
/* fallthrough */
default:
sendit:
BUFFER_BYTE (lastsent = c);
}
}
/*****************************************************************************/
/* Process FNAMEPKT of file to be received. Check for aborted-transfer */
/* recovery and solve filename collisions. Check for enough disk space. */
/* Return initial file data position to start receiving at, or -1 if error */
/* detected to abort file reception. Set Rxfname, Rxlen, Rxfile. */
/*****************************************************************************/
static long LOCALFUNC
procfname (void)
{
register char *p;
char linebuf[128], *fileinfo, *badfname;
long filestart, bytes;
FILE *abortlog;
struct stat f;
int i;
/*------------------------------------------------------------------------*/
/* Initialize for file reception */
/*------------------------------------------------------------------------*/
badfname = NULL;
Rxfname[0] = Resume_WaZOO = 0;
/*------------------------------------------------------------------------*/
/* Save info on WaZOO transfer in case of abort */
/*------------------------------------------------------------------------*/
(void) strcpy (Resume_name, fancy_str (Rxbuf));
fileinfo = strchr (Rxbuf, '\0') + 1;
p = strchr (fileinfo, '\0') + 1;
SharedCap = (byte) ((Rxblklen > p - Rxbuf) ? *p & OURCAP : 0);
/*------------------------------------------------------------------------*/
/* If this is a null FNAMEPKT, return OK immediately */
/*------------------------------------------------------------------------*/
if (!Rxbuf[0])
return 0L;
p = Rxbuf + strlen ((char *) Rxbuf) - 1; /* Find transmitted simple
* filename */
while (p >= Rxbuf && *p != '\\' && *p != '/' && *p != ':')
p--;
(void) strcpy (linebuf, ++p);
(void) strlwr (linebuf);
p = check_netfile (linebuf);
j_status ("#%s %s %s", MSG_TXT (M_RECEIVING), (p) ? p : " ", Rxbuf);
/*------------------------------------------------------------------------*/
/* Extract and validate filesize */
/*------------------------------------------------------------------------*/
Rxlen = -1;
Rxfiletime = 0;
if (sscanf (fileinfo, "%ld %lo", &Rxlen, &Rxfiletime) < 1 || Rxlen < 0)
{
j_status (MSG_TXT (M_NO_LENGTH));
return -1L;
}
#ifdef ANSI_TIME_T
Rxfiletime += ANSI_TIME_T_DELTA;
#endif
(void) sprintf (Resume_info, "%ld %lo", Rxlen, Rxfiletime);
/*------------------------------------------------------------------------*/
/* Check if this is a failed WaZOO transfer which should be resumed */
/*------------------------------------------------------------------------*/
if (dexists (Abortlog_name))
{
errno = 0;
abortlog = fopen (Abortlog_name, read_ascii);
if (abortlog != (FILE *) NULL)
errno = 0;
if (!j_error (MSG_TXT (M_OPEN_MSG), Abortlog_name))
{
while (!feof (abortlog))
{
linebuf[0] = '\0';
if (!fgets (p = linebuf, sizeof (linebuf), abortlog))
break;
while (*p >= ' ')
++p;
*p = '\0';
p = strchr (linebuf, ' ');
*p = '\0';
if (!stricmp (linebuf, Resume_name))
{
p = strchr ((badfname = ++p), ' ');
*p = '\0';
if (!stricmp (++p, Resume_info))
{
++Resume_WaZOO;
break;
}
}
}
errno = 0;
(void) fclose (abortlog);
}
}
/*------------------------------------------------------------------------*/
/* Open either the old or a new file, as appropriate */
/*------------------------------------------------------------------------*/
p = strchr (strcpy (Rxfname, CURRENT.sc_Inbound), '\0');
errno = 0;
if (Resume_WaZOO)
{
(void) strcpy (p, badfname);
Rxfile = open (Rxfname, O_CREAT | O_RDWR | O_BINARY, S_IREAD | S_IWRITE);
}
else
{
(void) strcpy (p, Rxbuf);
/*---------------------------------------------------------------------*/
/* If the file already exists: */
/* 1) And the new file has the same time and size, skip it */
/* 2) And OVERWRITE is turned on, delete the old copy */
/* 3) Else create a unique file name in which to store new data */
/*---------------------------------------------------------------------*/
if (dexists (Rxfname))
{
(void) stat (Rxfname, &f);
if (Rxlen == f.st_size && (time_t) Rxfiletime == f.st_mtime)
{
j_status (MSG_TXT (M_ALREADY_HAVE), Rxfname);
return -1L;
}
i = strlen (Rxfname) - 1;
if ((!overwrite) || (is_arcmail (Rxfname, i)))
{
unique_name (Rxfname);
j_status (MSG_TXT (M_RENAME_MSG), Rxfname);
}
else
{
(void) unlink (Rxfname);
}
}
Rxfile = open (Rxfname, O_CREAT | O_EXCL | O_RDWR | O_BINARY, S_IREAD | S_IWRITE);
}
if (Rxfile != -1)
errno = 0;
if (j_error (MSG_TXT (M_OPEN_MSG), Rxfname))
return -1L;
if (isatty (Rxfile)) /* Check for character devices */
{
(void) close (Rxfile); /* Return errors if it is the case */
errno = 1;
(void) j_error (MSG_TXT (M_DEVICE_MSG), Rxfname);
return -1L;
}
/*------------------------------------------------------------------------*/
/* Determine initial file data position */
/*------------------------------------------------------------------------*/
if (Resume_WaZOO)
{
(void) stat (Rxfname, &f);
j_status (MSG_TXT (M_SYNCHRONIZING_OFFSET), filestart = f.st_size);
p = Rxbuf;
errno = 0;
if (lseek (Rxfile, filestart, SEEK_SET) == -1L)
{
(void) j_error (MSG_TXT (M_SEEK_MSG), Rxfname);
(void) close (Rxfile);
return -1L;
}
}
else
filestart = 0L;
/*------------------------------------------------------------------------*/
/* Check for enough disk space */
/*------------------------------------------------------------------------*/
bytes = Rxlen - filestart + 10240;
if (bytes > Diskavail)
{
j_status (MSG_TXT (M_OUT_OF_DISK_SPACE));
(void) close (Rxfile);
return -1L;
}
/*------------------------------------------------------------------------*/
/* Print status message for the sysop */
/*------------------------------------------------------------------------*/
if (!un_attended || !fullscreen)
Rx_y = Next_y;
else
Rx_y = 2;
xfer_summary (MSG_TXT (M_RECV), p, &Rxlen, Rx_y);
(void) time ((time_t *) & Rxsttime);
return filestart;
}
/*****************************************************************************/
/* Receive, validate, and extract a packet if available. If a complete */
/* packet hasn't been received yet, receive and store as much of the next */
/* packet as possible. Each call to rcvpkt() will continue accumulating a */
/* packet until a complete packet has been received or an error is detected. */
/* Rxbuf must not be modified between calls to rcvpkt() if NOPKT is returned.*/
/* Returns type of packet received, NOPKT, or BADPKT. Sets Rxblklen. */
/*****************************************************************************/
static byte LOCALFUNC
rcvpkt ()
{
static byte rxcrc32;
static word crc;
static unsigned long crc32;
register byte *p;
register int c;
int i;
unsigned long pktcrc;
/*------------------------------------------------------------------------*/
/* Abort transfer if operator pressed ESC */
/*------------------------------------------------------------------------*/
if (got_ESC ())
{
j_status (GenericError, MSG_TXT (M_KBD_MSG));
return HALTPKT;
}
/*------------------------------------------------------------------------*/
/* If not accumulating packet yet, find start of next packet */
/*------------------------------------------------------------------------*/
WaitFlag = FALSE;
p = Rxbufptr;
if (!p)
{
do
c = rxbyte ();
while (c >= 0 || c == PKTEND);
switch (c)
{
case PKTSTRT:
rxcrc32 = FALSE;
p = (byte *) Rxbuf;
crc = 0;
break;
case PKTSTRT32:
rxcrc32 = TRUE;
p = (byte *) Rxbuf;
crc32 = 0xFFFFFFFF;
break;
case NOCARRIER:
j_status (GenericError, &(MSG_TXT (M_NO_CARRIER)[1]));
return HALTPKT;
default:
return NOPKT;
}
}
/*------------------------------------------------------------------------*/
/* Accumulate packet data until we empty buffer or find packet delimiter */
/*------------------------------------------------------------------------*/
if (rxcrc32)
{
while ((c = rxbyte ()) >= 0 && p < Rxbufmax)
{
*p++ = (byte) c;
crc32 = Z_32UpdateCRC (c, crc32);
}
}
else
{
while ((c = rxbyte ()) >= 0 && p < Rxbufmax)
{
*p++ = (byte) c;
crc = xcrc (crc, c);
}
}
/*------------------------------------------------------------------------*/
/* Handle whichever end-of-packet condition occurred */
/*------------------------------------------------------------------------*/
switch (c)
{
/*---------------------------------------------------------------------*/
/* PKTEND found; verify valid CRC */
/*---------------------------------------------------------------------*/
case PKTEND:
WaitFlag = TRUE;
pktcrc = 0;
for (i = (rxcrc32) ? 4 : 2; i; --i)
{
if ((c = rxbyte ()) < 0)
break;
pktcrc = (pktcrc << 8) | c;
}
if (!i)
{
if ((rxcrc32 && pktcrc == crc32) || pktcrc == crc)
{
/*------------------------------------------------------------*/
/* Good packet verified; compute packet data length and */
/* return packet type */
/*------------------------------------------------------------*/
Rxbufptr = NULL;
Rxblklen = (int) (--p - (byte *) Rxbuf);
return *p;
}
}
/* fallthrough */
/*---------------------------------------------------------------------*/
/* Bad CRC, carrier lost, or buffer overflow from munged PKTEND */
/*---------------------------------------------------------------------*/
default:
if (c == NOCARRIER)
{
j_status (GenericError, &(MSG_TXT (M_NO_CARRIER)[1]));
return HALTPKT;
}
else
{
Rxbufptr = NULL;
return BADPKT;
}
/*---------------------------------------------------------------------*/
/* Emptied buffer; save partial packet and let sender do something */
/*---------------------------------------------------------------------*/
case BUFEMPTY:
time_release (); /* Also give other tasks a chance */
Rxbufptr = p;
return NOPKT;
/*---------------------------------------------------------------------*/
/* PKTEND was trashed; discard partial packet and prep for next one */
/*---------------------------------------------------------------------*/
case PKTSTRT:
rxcrc32 = FALSE;
Rxbufptr = (byte *) Rxbuf;
crc = 0;
return BADPKT;
case PKTSTRT32:
rxcrc32 = TRUE;
Rxbufptr = (byte *) Rxbuf;
crc32 = 0xFFFFFFFF;
return BADPKT;
}
}
/*****************************************************************************/
/* Close file being received and perform post-reception aborted-transfer */
/* recovery cleanup if neccessary. */
/*****************************************************************************/
static void LOCALFUNC
rxclose (word xfer_flag)
{
register char *p;
char namebuf[PATHLEN], linebuf[128];
byte c;
FILE *abortlog, *newlog;
struct utimbuf utimes;
/*------------------------------------------------------------------------*/
/* Close file we've been receiving */
/*------------------------------------------------------------------------*/
errno = 0;
(void) close (Rxfile);
if (Rxfiletime > 0) /* utime doesn't like negative numbers */
{
utimes.UT_ACTIME = Rxfiletime;
utimes.modtime = Rxfiletime;
(void) utime (Rxfname, (UTIMBUF *) & utimes);
}
/*------------------------------------------------------------------------*/
/* If we completed a previously-aborted transfer, kill log entry & rename */
/*------------------------------------------------------------------------*/
if (xfer_flag == GOOD_XFER && Resume_WaZOO)
{
abortlog = fopen (Abortlog_name, read_ascii);
if (abortlog != (FILE *) NULL)
errno = 0;
if (!j_error (MSG_TXT (M_OPEN_MSG), Abortlog_name))
{
c = 0;
(void) strcpy (strchr (strcpy (namebuf, Abortlog_name), '\0') - 1, "TMP");
newlog = fopen (namebuf, write_ascii);
if (newlog != (FILE *) NULL)
errno = 0;
if (!j_error (MSG_TXT (M_OPEN_MSG), namebuf))
{
while (!feof (abortlog))
{
linebuf[0] = '\0';
if (!fgets (p = linebuf, sizeof (linebuf), abortlog))
break;
while (*p > ' ')
++p;
*p = '\0';
if (stricmp (linebuf, Resume_name))
{
*p = ' ';
(void) fputs (linebuf, newlog);
if (j_error (MSG_TXT (M_WRITE_MSG), namebuf))
break;
++c;
}
}
errno = 0;
(void) fclose (abortlog);
(void) fclose (newlog);
(void) unlink (Abortlog_name);
if (c)
{
if (!rename (namebuf, Abortlog_name))
errno = 0;
(void) j_error (MSG_TXT (M_RENAME_MSG), namebuf);
}
else
{
(void) unlink (namebuf);
}
}
else
{
(void) fclose (abortlog);
}
}
j_status (MSG_TXT (M_FINISHED_PART), Resume_name);
unique_name (strcat (strcpy (namebuf, CURRENT.sc_Inbound), Resume_name));
if (!rename (Rxfname, namebuf))
{
errno = 0;
(void) strcpy (Rxfname, namebuf);
}
else
(void) j_error (MSG_TXT (M_RENAME_MSG), Rxfname);
/*------------------------------------------------------------------------*/
/* If transfer failed and was not an attempted resumption, log for later */
/*------------------------------------------------------------------------*/
}
else if (xfer_flag == FAILED_XFER && !Resume_WaZOO)
{
j_status (MSG_TXT (M_SAVING_PART), Rxfname);
unique_name (strcat (strcpy (namebuf, CURRENT.sc_Inbound), "BadWaZOO.001"));
if (!rename (Rxfname, namebuf))
errno = 0;
(void) j_error (MSG_TXT (M_RENAME_MSG), Rxfname);
abortlog = fopen (Abortlog_name, append_ascii);
if (abortlog != (FILE *) NULL)
errno = 0;
if (!j_error (MSG_TXT (M_OPEN_MSG), Abortlog_name))
{
(void) fprintf (abortlog, "%s %s %s\n", Resume_name, namebuf + strlen (CURRENT.sc_Inbound), Resume_info);
(void) j_error (MSG_TXT (M_WRITE_MSG), Abortlog_name);
(void) fclose (abortlog);
}
else
{
(void) unlink (namebuf);
}
}
}
/*****************************************************************************/
/* Try REAL HARD to disengage batch session cleanly */
/*****************************************************************************/
static void LOCALFUNC
endbatch (void)
{
register int done, timeouts;
long timeval, brain_dead;
/*------------------------------------------------------------------------*/
/* Tell the other end to halt if it hasn't already */
/*------------------------------------------------------------------------*/
done = timeouts = 0;
long_set_timer (&brain_dead, 120);
sendpkt (NULL, 0, HALTPKT);
long_set_timer (&timeval, TimeoutSecs);
/*------------------------------------------------------------------------*/
/* Wait for the other end to acknowledge that it's halting */
/*------------------------------------------------------------------------*/
while (!done)
{
if (long_time_gone (&brain_dead))
break;
switch (rcvpkt ())
{
case NOPKT:
case BADPKT:
if (long_time_gone (&timeval))
{
if (++timeouts > 2)
++done;
else
goto reject;
}
break;
case HALTPKT:
case HALTACKPKT:
++done;
break;
default:
timeouts = 0;
reject:
sendpkt (NULL, 0, HALTPKT);
long_set_timer (&timeval, TimeoutSecs);
break;
}
}
/*------------------------------------------------------------------------*/
/* Announce quite insistently that we're done now */
/*------------------------------------------------------------------------*/
for (done = 0; done < 10; ++done)
sendpkt (NULL, 0, HALTACKPKT);
while (!OUT_EMPTY())
time_release ();
}
/*****************************************************************************/
/* Print a message in the message field of a transfer status line */
/*****************************************************************************/
void
j_message (unsigned int pos, char *va_alist,...)
{
va_list arg_ptr;
short y, l;
char buf[128];
y = pos;
va_start (arg_ptr, va_alist);
if (!un_attended || !fullscreen)
gotoxy (MSG_X, y);
else
sb_move (filewin, y, MSG_X);
(void) vsprintf (buf, va_alist, arg_ptr);
for (l = 25 - strlen (buf); l > 0; --l)
(void) strcat (buf, " ");
if (!un_attended || !fullscreen)
{
(void) cputs (buf);
}
else
{
sb_puts (filewin, buf);
sb_show ();
}
va_end (arg_ptr);
}
/*****************************************************************************/
/* Clear out a line in the log status display */
/*****************************************************************************/
void
j_msgend (short pos)
{
if (un_attended && fullscreen)
{
sb_move (filewin, pos, 2);
/* 72 blanks */
sb_puts (filewin, " ");
sb_show ();
}
}
/*****************************************************************************/
/* Print & log status message without messing up display */
/*****************************************************************************/
void
j_status (char *va_alist,...)
{
va_list arg_ptr;
char buf[128];
va_start (arg_ptr, va_alist);
if (!un_attended || !fullscreen)
gotoxy (1, Next_y - 1);
(void) vsprintf (buf, va_alist, arg_ptr);
status_line (buf);
if (!un_attended || !fullscreen)
update_y ();
va_end (arg_ptr);
}
/*****************************************************************************/
/* Print & log error message without messing up display */
/*****************************************************************************/
int
j_error (char *msg, char *fname)
{
register int e;
if ((e = (int) errno) != 0)
{
if (!un_attended || !fullscreen)
gotoxy (1, Next_y - 1);
(void) got_error (msg, fname);
if (!un_attended || !fullscreen)
update_y ();
}
return e;
}
/*****************************************************************************/
/* Update screen position variables after printing a message */
/*****************************************************************************/
static void LOCALFUNC
update_y ()
{
set_xy (NULL); /* Bump cursor to next line after printing */
if (locate_y == Next_y)
{ /* If we didn't go anywhere, screen scrolled;*/
if (Tx_y > 1) /* so decrement status line numbers */
--Tx_y;
if (Rx_y > 1)
--Rx_y;
}
else
Next_y = locate_y;
}
/*****************************************************************************/
/* Compute future timehack for later reference */
/*****************************************************************************/
static void LOCALFUNC
long_set_timer (long *Buffer, unsigned int Duration)
{
(void) time ((time_t *) Buffer);
*Buffer += (long) Duration;
}
/*****************************************************************************/
/* Return TRUE if timehack has been passed, FALSE if not */
/*****************************************************************************/
static int LOCALFUNC
long_time_gone (long *TimePtr)
{
return (time (NULL) > (time_t) * TimePtr);
}
/*****************************************************************************/
/* Receive cooked escaped byte translated to avoid various problems. */
/* Returns raw byte, BUFEMPTY, PKTSTRT, PKTEND, or NOCARRIER. */
/*****************************************************************************/
static int LOCALFUNC
rxbyte (void)
{
register int c, w;
if ((c = rcvrawbyte ()) == DLE)
{
w = WaitFlag++;
if ((c = rcvrawbyte ()) >= 0)
{
switch (c ^= 0x40)
{
case PKTSTRTCHR:
c = PKTSTRT;
break;
case PKTSTRTCHR32:
c = PKTSTRT32;
break;
case PKTENDCHR:
c = PKTEND;
break;
}
}
WaitFlag = (byte) w;
}
return c;
}
/*****************************************************************************/
/* Receive raw non-escaped byte. Returns byte, BUFEMPTY, or NOCARRIER. */
/* If waitflag is true, will wait for a byte for Timeoutsecs; otherwise */
/* will return BUFEMPTY if a byte isn't ready and waiting in inbound buffer. */
/*****************************************************************************/
static int LOCALFUNC
rcvrawbyte (void)
{
long timeval;
if ((int) PEEKBYTE () >= 0)
return MODEM_IN ();
if (!CARRIER)
return NOCARRIER;
if (!WaitFlag)
return BUFEMPTY;
timeval = time (NULL) + TimeoutSecs;
while ((int) PEEKBYTE () < 0)
{
if (!CARRIER)
return NOCARRIER;
if (time (NULL) > (time_t) timeval)
return BUFEMPTY;
time_release ();
}
return MODEM_IN ();
}
/*****************************************************************************/
/* Display start-of-transfer summary info */
/*****************************************************************************/
void
xfer_summary (char *xfertype, char *fname, long *len, short y)
{
char buf[128];
if (!un_attended || !fullscreen)
gotoxy (2, y);
else
sb_move (filewin, y, 2);
(void) sprintf (buf, "%s %12.12s; 0/%8ldb,%4ld min. ",
xfertype, fname, *len, ((*len * 10L / cur_baud.rate_value * 100L / JANUS_EFFICIENCY + 59L) / 60L));
if (!un_attended || !fullscreen)
{
(void) cputs (buf);
(void) cputs (local_CEOL);
update_y ();
}
else
{
sb_puts (filewin, buf);
sb_show ();
}
}
/*****************************************************************************/
/* Update any status line values which have changed */
/*****************************************************************************/
void
update_status (long *pos, long *oldpos, long left, int *oldeta, short y)
{
char buf[16];
register int eta;
elapse_time ();
if (*pos != *oldpos)
{
(void) sprintf (buf, "%8ld", *oldpos = *pos);
if (!un_attended || !fullscreen)
{
gotoxy (POS_X, y);
(void) cputs (buf);
}
else
{
sb_move (filewin, y, POS_X);
sb_puts (filewin, buf);
}
}
eta = (int) ((left * 10L / cur_baud.rate_value * 100L / JANUS_EFFICIENCY + 59L) / 60L);
if (eta != *oldeta)
{
(void) sprintf (buf, "%4d Min", *oldeta = eta);
if (!un_attended || !fullscreen)
{
gotoxy (ETA_X, y);
(void) cputs (buf);
}
else
{
sb_move (filewin, y, ETA_X);
sb_puts (filewin, buf);
}
}
if (un_attended && fullscreen)
sb_show ();
}
/*****************************************************************************/
/* Compute and print throughput */
/*****************************************************************************/
long
through (long *bytes, long *started)
{
static char *scrn = "+CPS: %u (%lu bytes) Efficiency: %lu%%%%";
unsigned long elapsed;
register word cps;
elapsed = time (NULL) - *started;
cps = (elapsed) ? (word) (*bytes / elapsed) : 0;
j_status (scrn, cps, *bytes, cps * 1000L / cur_baud.rate_value);
return elapsed;
}
/*****************************************************************************/
/* Get next file to request, if any */
/*****************************************************************************/
static int LOCALFUNC
get_filereq (byte req_started)
{
char reqname[PATHLEN], linebuf[128], *holdname;
register char *p;
int gotone = FALSE;
FILE *reqfile;
next_aka:
if (!EMSI_flag)
{
holdname = HoldAreaNameMunge (&called_addr);
(void) sprintf (reqname,
"%s%s.REQ\0",
holdname,
Hex_Addr_Str (&called_addr));
}
else
{
holdname = HoldAreaNameMunge (&remote_akas[EMSI_raka]);
(void) sprintf (reqname,
"%s%s.REQ\0",
holdname,
Hex_Addr_Str (&remote_akas[EMSI_raka]));
}
if (req_started)
mark_done (reqname);
if (dexists (reqname))
{
if (!(remote_capabilities & WZ_FREQ))
j_status (MSG_TXT (M_FREQ_DECLINED));
else if (!(SharedCap & CANFREQ))
j_status (MSG_TXT (M_REMOTE_CANT_FREQ));
else
{
errno = 0;
reqfile = fopen (reqname, read_ascii);
if (reqfile != (FILE *) NULL)
errno = 0;
if (!j_error (MSG_TXT (M_OPEN_MSG), reqname))
{
while (!feof (reqfile))
{
linebuf[0] = '\0';
if (!fgets (p = linebuf, sizeof (linebuf), reqfile))
break;
while (*p >= ' ')
++p;
*p = '\0';
if (linebuf[0] != ';')
{
(void) strcpy (Rxbuf, linebuf);
*(strchr (Rxbuf, '\0') + 1) = SharedCap;
gotone = TRUE;
break;
}
}
errno = 0;
(void) fclose (reqfile);
if (!gotone)
{
(void) unlink (reqname);
}
}
}
}
if (!gotone && EMSI_flag)
{
if (++EMSI_raka < num_rakas)
goto next_aka;
}
return gotone;
}
/*****************************************************************************/
/* Record names of files to send in response to file request; callback */
/* routine for respond_to_file_requests() */
/*****************************************************************************/
static int
record_reqfile (char *fname)
{
FILE *pFileT;
errno = 0;
pFileT = fopen (ReqTmp, append_ascii);
if (pFileT != (FILE *) NULL)
errno = 0;
if (!j_error (MSG_TXT (M_OPEN_MSG), ReqTmp))
{
errno = 0;
(void) fputs (fname, pFileT);
(void) j_error (MSG_TXT (M_WRITE_MSG), ReqTmp);
(void) fputs ("\n", pFileT);
(void) j_error (MSG_TXT (M_WRITE_MSG), ReqTmp);
(void) fclose (pFileT);
++ReqRecorded;
return TRUE;
}
return FALSE;
}
/*****************************************************************************/
/* Estimate transfer time for requested file(s); callback */
/* routine for respond_to_file_requests() */
/*****************************************************************************/
static int
timeof_reqfile (long filesize)
{
int i;
i = (int) (filesize * 10L / cur_baud.rate_value * 100L / JANUS_EFFICIENCY);
/*
* Since actual transfers don't occur while in file request code, we have to "reverse engineer" the testing for end-time.
*/
if (CURRENT.time_Limit != 0)
{
if (((long) time (NULL) + i - freq_accum.time) > CURRENT.time_Limit)
return i;
freq_accum.time -= i; /* Add time to (now-start) interval */
return 0;
}
return i;
}
/*****************************************************************************/
/* Get next file which was requested, if any */
/*****************************************************************************/
static byte LOCALFUNC
get_reqname (byte first_req)
{
register char *p;
byte gotone = FALSE;
FILE *pFileT;
struct stat f;
if (!first_req)
{
errno = 0;
(void) close (Txfile);
Txfile = -1;
mark_done (ReqTmp);
}
if (dexists (ReqTmp))
{
errno = 0;
pFileT = fopen (ReqTmp, read_ascii);
if (pFileT != (FILE *) NULL)
errno = 0;
if (!j_error (MSG_TXT (M_OPEN_MSG), ReqTmp))
{
while (!feof (pFileT))
{
Txfname[0] = '\0';
if (!fgets (p = Txfname, PATHLEN, pFileT))
break;
while (*p >= ' ')
++p;
*p = '\0';
if (Txfname[0] != ';')
{
j_status (MSG_TXT (M_SENDING), Txfname);
errno = 0;
Txfile = share_open (Txfname, O_RDONLY | O_BINARY, DENY_WRITE);
if (Txfile != -1)
errno = 0;
if (j_error (MSG_TXT (M_OPEN_MSG), Txfname))
continue;
while (p >= Txfname && *p != '\\' && *p != ':')
--p;
(void) strcpy ((char *) Txbuf, ++p);
(void) stat (Txfname, &f);
#ifdef ANSI_TIME_T
f.st_mtime -= ANSI_TIME_T_DELTA;
#endif
(void) sprintf (strchr ((char *) Txbuf, '\0') + 1, "%lu %lo %o", Txlen = f.st_size, f.st_mtime, f.st_mode);
if (!un_attended || !fullscreen)
Tx_y = Next_y;
else
Tx_y = 1;
xfer_summary (MSG_TXT (M_SEND), p, &Txlen, Tx_y);
(void) time ((time_t *) & Txsttime);
gotone = TRUE;
break;
}
}
(void) fclose (pFileT);
if (!gotone)
{
(void) unlink (ReqTmp);
}
}
}
return gotone;
}
/*****************************************************************************/
/* Mark first unmarked line of file as done (comment it out) */
/*****************************************************************************/
static void LOCALFUNC
mark_done (char *fname)
{
char linebuf[128];
FILE *fh;
long pos;
if (dexists (fname))
{
errno = 0;
fh = fopen (fname, "rb+");
if (fh != (FILE *) NULL)
errno = 0;
if (!j_error (MSG_TXT (M_OPEN_MSG), fname))
{
while (!feof (fh))
{
pos = ftell (fh);
if (pos == -1L)
(void) j_error (MSG_TXT (M_SEEK_MSG), fname);
if (!fgets (linebuf, sizeof (linebuf), fh))
break;
if (linebuf[0] != ';')
{
if (fseek (fh, pos, SEEK_SET) == -1L)
(void) j_error (MSG_TXT (M_SEEK_MSG), fname);
(void) fputc (';', fh);
(void) j_error (MSG_TXT (M_WRITE_MSG), fname);
break;
}
}
(void) fclose (fh);
}
}
}